﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Xml;

using AL.Entity;
using AL.DAL.PublicHistoryPage;
using mshtml;
using Newtonsoft.Json;

namespace AL.Helper
{

    #region Model Document

    [Serializable]
    public class Document
    {
        public Item Item{get;set;}

        private void ReadXmlInternal(XmlReader reader)
        {
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.EndElement && reader.Name.ToUpper() == "DOCUMENT")
                {
                    break;
                }
                if (reader.NodeType == XmlNodeType.Element && reader.Name.ToLower() == "item")
                {
                    var item = new Item();
                    this.Item = item;
                    item.ReadXmlInternal(reader);
                    break;
                }
            }
        }

        public static Document ReadXml(XmlReader reader)
        {
            var document = new Document();
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element && reader.Name.ToUpper() == "DOCUMENT")
                {
                    document.ReadXmlInternal(reader);
                    break;
                }
            }
            return document;
        }
    }

    #endregion

    #region Model Item

    [Serializable]
    public class Item
    {
        public Display Display{get;set;}

        public string Key{get;set;}

        public string Tplid{get;set;}

        public string Classid{get;set;}

        internal void ReadXmlInternal(XmlReader reader)
        {
            var flag = true;
            while (!flag || reader.Read())
            {
                flag = true;
                if (reader.NodeType == XmlNodeType.EndElement && reader.Name.ToLower() == "item")
                {
                    break;
                }
                if (reader.NodeType == XmlNodeType.Element)
                {
                    var tagName = reader.Name.ToLower();
                    if (!string.IsNullOrWhiteSpace(tagName))
                    {
                        if (tagName != "display")
                        {
                            if (tagName != "key")
                            {
                                if (tagName != "tplid")
                                {
                                    if (tagName == "classid")
                                    {
                                        this.Classid = reader.ReadElementContentAsString();
                                        flag = false;
                                    }
                                }
                                else
                                {
                                    this.Tplid = reader.ReadElementContentAsString();
                                    flag = false;
                                }
                            }
                            else
                            {
                                this.Key = reader.ReadElementContentAsString();
                                flag = false;
                            }
                        }
                        else
                        {
                            var display = new Display();
                            display.ReadXmlInternal(reader);
                            this.Display = display;
                        }
                    }
                    continue;
                }
            }
        }
    }

    #endregion

    #region Model Dislpay

    [Serializable]
    public class Display
    {
        public string Docid{get;set;}

        public string Tplid{get;set;}

        public string Title{get;set;}

        public string Url{get;set;}

        public string Title1{get;set;}

        public string Imglink{get;set;}

        public string HeadImage{get;set;}

        public string Sourcename{get;set;}

        public string Content168{get;set;}

        public string Site{get;set;}

        public bool IsV{get;set;}

        public string Openid{get;set;}

        public string Content{get;set;}

        public string Showurl{get;set;}

        public DateTime Date{get;set;}

        public string Pagesize{get;set;}

        public long LastModified{get;set;}

        internal void ReadXmlInternal(XmlReader reader)
        {
            bool flag = true;
            while (!flag || reader.Read())
            {
                flag = true;
                if (reader.NodeType == XmlNodeType.EndElement && reader.Name.ToLower() == "display")
                {
                    break;
                }
                if (reader.NodeType == XmlNodeType.Element)
                {
                    var tagName = reader.Name.ToLower();
                    switch (tagName)
                    {
                        case "docid":
                            this.Docid = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "tplid":
                            this.Tplid = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "title":
                            this.Title = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "url":
                            this.Url = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "title1":
                            this.Title1 = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "imglink":
                            this.Imglink = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "headimage":
                            this.HeadImage = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "sourcename":
                            this.Sourcename = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "content168":
                            this.Content168 = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "site":
                            this.Site = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "isv":
                            this.IsV = reader.ReadElementContentAsBoolean();
                            flag = false;
                            break;
                        case "openid":
                            this.Openid = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "content":
                            this.Content = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "showurl":
                            this.Showurl = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "date":
                            this.Date = DateTime.Parse(reader.ReadElementContentAsString());
                            flag = false;
                            break;
                        case "pagesize":
                            this.Pagesize = reader.ReadElementContentAsString();
                            flag = false;
                            break;
                        case "lastmodified":
                            this.LastModified = reader.ReadElementContentAsLong();
                            flag = false;
                            break;
                    }
                    continue;
                }
            }
        }
    }

    #endregion

    #region Model HistoryPage

    //public class HistoryPage
    //{
    //    [JsonProperty("items")]
    //    public string[] Items { get; set; }

    //    [JsonProperty("page")]
    //    public int Page { get; set; }

    //    [JsonProperty("totalItems")]
    //    public int TotalItems { get; set; }

    //    [JsonProperty("totalPages")]
    //    public int TotalPages { get; set; }
    //}

    #endregion

    public class SogoArticleDownloader
    {
        public const string URL = "http://weixin.sogou.com/gzhjs?openid={0}&page={1}";
        public CancellationToken CancellationToken { get; set; }
        public SendOrPostCallback Callback { get; set; }

        //protected IArticleController articleController;
        public SogoArticleDownloader()
        {
            //this.articleController = ServiceLocator.Current.GetInstance<IArticleController>();
        }


        //public virtual void Download(Account account, DateTime startDate, DateTime endDate)
        //{
        //    if (this.Callback != null)
        //    {
        //        AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation(null);
        //    }
        //    if (string.IsNullOrWhiteSpace(account.OpenId))
        //    {
        //        throw new Exception("搜狗OpenId不能为空。");
        //    }
        //    using (var webClient = new WebClient())
        //    {
        //        webClient.Encoding = Encoding.UTF8;
        //        string value = this.GetCookie(account);
        //        webClient.Headers.Add(HttpRequestHeader.Cookie, value);


        //        var articleCollection = new ArticleCollection();
        //        int count = 0;
        //        bool flag = true;

        //        string url = string.Format("http://weixin.sogou.com/gzhjs?openid={0}&page={1}", account.OpenId, ++count);
        //        HistoryPage historyPage = this.GetHistoryPage(webClient, url);
        //        if (historyPage == null || historyPage.Items.Length == 0)
        //        {
        //            //break;
        //        }

        //        //while (flag)
        //        //{
        //        //    string url = string.Format("http://weixin.sogou.com/gzhjs?openid={0}&page={1}", account.OpenId, ++count);
        //        //    HistoryPage historyPage = this.GetHistoryPage(webClient, url);
        //        //    if (historyPage == null || historyPage.Items.Length == 0)
        //        //    {
        //        //        break;
        //        //    }

        //        //    int num3;
        //        //    int num2 = num3;
        //        //    int num4;
        //        //    string[] items;

        //        //    if (num2 != 2)
        //        //    {
        //        //        items = historyPage.Items;
        //        //        num4 = 0;
        //        //        goto IL_4F2;
        //        //    }
        //        //    goto IL_30C;
        //        //    IL_509:
        //        //    bool flag2;
        //        //    if (flag2)
        //        //    {
        //        //    }
        //        //    continue;
        //        //    Block_9:
        //        //    string text;
        //        //    StringReader stringReader;

        //        //IL_30C:
        //        //    try
        //        //    {
        //        //        Article article = this.DecodeArticleFromXML(text);
        //        //        if (article.ArticleCreated < startDate)
        //        //        {
        //        //            flag = false;
        //        //            goto IL_509;
        //        //        }
        //        //        if (!(article.ArticleCreated > endDate))
        //        //        {
        //        //            if (articleCollection.Contains(article.ArticleId))
        //        //            {
        //        //                flag = false;
        //        //                goto IL_509;
        //        //            }
        //        //            article.AccountId = account.Id;
        //        //            //await this.articleController.InsertOrModifyArticles(new Article[]
        //        //            //                                                    {
        //        //            //                                                        article
        //        //            //                                                    }, false);

        //        //            articleCollection.Add(article);
        //        //            Debug.WriteLine(string.Format("{0} {2}({1}) {3}", new object[]
        //        //                                                              {
        //        //                                                                  article.ArticleCreated,
        //        //                                                                  article.AccountId,
        //        //                                                                  article.AccountName,
        //        //                                                                  article.Title
        //        //                                                              }));
        //        //        }
        //        //    }
        //        //    finally
        //        //    {
        //        //        if (stringReader != null)
        //        //        {
        //        //            ((IDisposable) stringReader).Dispose();
        //        //        }
        //        //    }
        //        //    IL_4E3:
        //        //    num4++;
        //        //    goto IL_4F2;
        //        //    goto IL_4E3;
        //        //    IL_4F2:
        //        //    if (num4 >= items.Length)
        //        //    {
        //        //        goto IL_509;
        //        //    }
        //        //    text = items[num4];
        //        //    CancellationToken arg_2B1_0 = this.CancellationToken;
        //        //    bool flag3 = 1 == 0;
        //        //    this.CancellationToken.WaitHandle.WaitOne();
        //        //    if (this.CancellationToken.IsCancellationRequested)
        //        //    {
        //        //        flag = false;
        //        //        goto IL_509;
        //        //    }
        //        //    stringReader = new StringReader(text);
        //        //    goto Block_9;
        //        //}
        //    }
        //}


        public List<Article> GetArticleList(Account account, int pageIndex)
        {
            var articleCollection = new List<Article>();
            var historyPage = GetHistoryPage(account.OpenId, EnumType.SearchType.Week);
            if (historyPage != null)
            {
                //var existingArticles = DAL.PublicArticle.Provider.GetByOpenId(account.OpenId);
                foreach (var item in historyPage.Items)
                {
                    var article = this.GetArticle(item);
                    //if (existingArticles.All(x => x.ArticleId != article.ArticleId))
                    //{
                    //    // 缓存数据
                    //    DAL.PublicArticle.Provider.Add(article);
                    //    // 异步查询点赞统计数

                    //}
                    articleCollection.Add(article);
                }
            }

            return articleCollection;
        }

        public Article GetArticle(string xmlText)
        {
            var article = this.DecodeArticleFromXml(xmlText);
            return article;
        }

        public List<Article> GetArticlesByHistoryPage(HistoryPage historyPage)
        {
            var articleCollection = new List<Article>();
            if (historyPage != null)
            {
                foreach (var item in historyPage.Items)
                {
                    articleCollection.Add(this.GetArticle(item));
                }
            }

            return articleCollection;
        }

        public HistoryPage GetHistoryPage(string openId, EnumType.SearchType searchType)
        {
            if (string.IsNullOrWhiteSpace(openId)){throw new Exception("搜狗OpenId不能为空。");}

            using (var webClient = new WebClient() { Encoding = Encoding.UTF8 })
            {
                var value = this.GetCookie(openId);
                webClient.Headers.Add(HttpRequestHeader.Cookie, value);

                var items = new List<string>();
                var baseUrl = "http://weixin.sogou.com/gzhjs?openid={0}&page={1}";

                HistoryPage historyPage = null;
                for (var i = 1; i < 21; i++)
                {
                    var url = string.Format("http://weixin.sogou.com/gzhjs?openid={0}&page={1}", openId, i);

                    historyPage = this.GetHistoryPage(webClient, url);
                    if (historyPage != null && historyPage.Items != null && historyPage.Items.Length > 0)
                    {
                        if (searchType == EnumType.SearchType.All)
                        {
                            // 加载所有记录
                            items.AddRange(historyPage.Items);
                        }else if (searchType == EnumType.SearchType.Week)
                        {
                            // 加载七天内的数据
                            var needBreak = false;
                            foreach (var item in historyPage.Items)
                            {
                                var article = this.GetArticle(item);
                                if (article != null && article.ArticleCreated.AddDays(7) > DateTime.Now) items.Add(item);
                                else
                                {
                                    needBreak = true;
                                    break;
                                };
                            }

                            if (needBreak) break;

                        }else if (searchType == EnumType.SearchType.Month)
                        {
                            // 加载一个月内的数据
                            var needBreak = false;
                            foreach (var item in historyPage.Items)
                            {
                                var article = this.GetArticle(item);
                                if (article != null && article.ArticleCreated.AddMonths(1) > DateTime.Now) items.Add(item);
                                else
                                {
                                    needBreak = true;
                                    break;
                                }
                            }

                            if (needBreak) break;
                        }
                        
                    }
                    else break;
                }

                if (historyPage != null) historyPage.Items = items.ToArray();

                return historyPage;
            }
        }

        #region GetCookie

        private static string _cookie = string.Empty;
        protected virtual string GetCookie(string openId)
        {
            if (!string.IsNullOrWhiteSpace(_cookie)) return _cookie;

            var httpWebRequest = (HttpWebRequest) WebRequest.Create("http://www.sogou.com");
            httpWebRequest.AllowAutoRedirect = false;
            httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko";
            httpWebRequest.Timeout = 2000;
            var httpWebResponse = (HttpWebResponse) (httpWebRequest.GetResponse());
            var cookieString = httpWebResponse.Headers["Set-Cookie"];
            var cookies = this.GetCookies(cookieString, "sogou.com");
            httpWebRequest = (HttpWebRequest) WebRequest.Create("http://weixin.sogou.com");
            httpWebRequest.AllowAutoRedirect = false;
            httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko";
            httpWebRequest.Referer = "http://www.sogou.com";
            httpWebRequest.CookieContainer = new CookieContainer();
            httpWebRequest.CookieContainer.Add(cookies);
            httpWebRequest.Timeout = 2000;
            httpWebResponse = (HttpWebResponse)(httpWebRequest.GetResponse());
            cookieString = httpWebResponse.Headers["Set-Cookie"];
            cookies.Add(this.GetCookies(cookieString, "weixin.sogou.com"));
            httpWebRequest = (HttpWebRequest)WebRequest.Create(string.Format("http://weixin.sogou.com/gzh?openid={0}", openId));
            httpWebRequest.AllowAutoRedirect = false;
            httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko";
            httpWebRequest.Referer = "http://weixin.sogou.com";
            httpWebRequest.CookieContainer = new CookieContainer();
            httpWebRequest.CookieContainer.Add(cookies);
            httpWebRequest.Timeout = 2000;
            httpWebResponse = (HttpWebResponse)(httpWebRequest.GetResponse());
            using (var streamReader = new StreamReader(httpWebResponse.GetResponseStream(), Encoding.UTF8))
            {
                string text2 = streamReader.ReadToEnd();
            }
            cookieString = httpWebResponse.Headers["Set-Cookie"];
            cookies.Add(this.GetCookies(cookieString, "weixin.sogou.com"));
            cookieString = this.GetCookiesString(cookies);
            cookieString = cookieString + "SUV=" + DateTime.Now.Ticks.ToString() + "111;";
            if (cookieString.IndexOf("SNUID") < 0)
            {
                cookieString += "SNUID=F3C2E6771316174FEFD200B213ACEF32;";
            }

            _cookie = cookieString;
            return cookieString;
        }

        private string GetCookiesString(CookieCollection cookies)
        {
            var stringBuilder = new StringBuilder();
            foreach (Cookie cookie in cookies)
            {
                stringBuilder.Append(this.GetCookieString(cookie));
                stringBuilder.Append(";");
            }
            return stringBuilder.ToString();
        }

        private string GetCookieString(Cookie cookie)
        {
            return string.Format("{0}={1}", cookie.Name, string.Format(cookie.Value, "%2C", ","));
        }

        private CookieCollection GetCookies(string cookiesString, string domain)
        {
            var cookieCollection = new CookieCollection();
            CookieCollection result;
            if (cookiesString == null)
            {
                result = cookieCollection;
            }
            else
            {
                string[] array = cookiesString.Split(new char[]{';'});
                string[] array2 = array;
                for (int i = 0; i < array2.Length; i++)
                {
                    string cookieString = array2[i];
                    if (!string.IsNullOrWhiteSpace(cookiesString))
                    {
                        Cookie cookie = this.ParseToCookie(cookieString, domain);
                        cookieCollection.Add(cookie);
                    }
                }
                result = cookieCollection;
            }
            return result;
        }

        private Cookie ParseToCookie(string cookieString, string domain)
        {
            int num = cookieString.IndexOf('=');
            string value = "";
            string text;
            if (num > 0)
            {
                text = cookieString.Substring(0, num);
                if (num < cookieString.Length - 1)
                {
                    value = cookieString.Substring(num + 1).Replace(",", "%2C");
                }
            }
            else
            {
                text = cookieString;
            }
            text = text.Trim();
            return new Cookie(text, value, "/", domain);
        }

        #endregion

        protected Article DecodeArticleFromXml(string articleXmlData)
        {
            Article result;
            using (StringReader stringReader = new StringReader(articleXmlData))
            {
                using (XmlReader xmlReader = XmlReader.Create(stringReader))
                {
                    Document document = Document.ReadXml(xmlReader);
                    Article article = this.ConverTo(document);
                    result = article;
                }
            }
            return result;
        }

        protected HistoryPage GetHistoryPage(WebClient webClient, string url)
        {
            string text = "";
            try
            {
                text = webClient.DownloadString(url);
            }
            catch (WebException innerException)
            {
                Debug.WriteLine("下载搜狗读取历史图文失败1：");
                Debug.WriteLine(url);
                //throw new Exception("下载搜狗读取历史图文失败1", innerException);
                return null;
            }
            int num = text.IndexOf("{");
            int num2 = text.LastIndexOf("}");
            if (num < 0 || num2 < 0)
            {
                Debug.WriteLine("搜狗读取历史图文失败2：");
                Debug.WriteLine(url);
                Debug.WriteLine(text);
                //throw new Exception("解析搜狗读取历史图文失败2") {Data = {{"Context", text}}};
                return null;
            }
            string value = text.Substring(num, num2 - num + 1);
            HistoryPage result = null;
            try
            {
                result = JsonConvert.DeserializeObject<HistoryPage>(value);
            }
            catch (Exception innerException2)
            {
                Debug.WriteLine("搜狗读取历史图文失败3：");
                Debug.WriteLine(url);
                Debug.WriteLine(text);
                //throw new Exception("解析搜狗读取历史图文失败3", innerException2) {Data = {{"Context", text}}};
                return null;
            }
            return result;
        }

        private Article ConverTo(Document document)
        {
            var display = document.Item.Display;
            return new Article
                   {
                       AccountId = string.Empty,
                       AccountName = display.Sourcename,
                       AccountHeaderIcon = new byte[0],
                       AccountHeaderIconURL = display.HeadImage,
                       ArticleId = display.Docid,
                       ArticlePicture = new byte[0],
                       ArticlePictureURL = display.Imglink,
                       ArticleURL = display.Url,
                       ArticleCreated = display.Date,
                       IsV = display.IsV,
                       Summary = string.IsNullOrWhiteSpace(display.Content168) ? display.Content : display.Content168,
                       PraiseCount = 0,
                       ReadingCount = 0,
                       Title = string.IsNullOrWhiteSpace(display.Title1) ? display.Title : display.Title1,
                       OpendId = display.Openid,
                       LastModified = display.LastModified
                   };
        }
    }
}
